#if !defined(AFX_JSCRIPTFORMAT_H__20000617_7F07_0D53_5A0C_0080AD509054__INCLUDED_)
#define AFX_JSCRIPTFORMAT_H__20000617_7F07_0D53_5A0C_0080AD509054__INCLUDED_
//
// Written by Bjarke Viksoe (bjarke@viksoe.dk)
// Copyright (c) 2000.
//
// This code may be used in compiled form in any way you desire. This
// file may be redistributed unmodified by any means PROVIDING it is 
// not sold for profit without the authors written consent, and 
// providing that this notice and the authors name is included. 
//
// This file is provided "as is" with no expressed or implied warranty.
// The author accepts no liability if it causes any damage to you or your
// computer whatsoever. It's free, so don't hassle me about it.
//
// Beware of bugs.
//

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

class CJScriptFormat
{
   typedef enum { 
      STATE_SCRIPT, 
      STATE_KEYWORD, 
      STATE_STRING, 
      STATE_COMMENT_SL,
      STATE_COMMENT_ML
   } STATE;

public:
   static CString Format(LPCTSTR pstrText)
   {
      ATLASSERT(pstrText);
      if( pstrText==NULL ) return _T("");

      CString s;
      DWORD dwSize = _tcslen(pstrText)*8;
      if( dwSize<4000 ) dwSize=8000;
      LPTSTR pstrTarget = s.GetBuffer(dwSize);
      LPTSTR pEnd = pstrTarget + dwSize - 256;
      LPTSTR pT = pstrTarget;

      STATE State = STATE_SCRIPT;
      TCHAR ch;
      TCHAR chString;
      bool bWasDelim;
      while( (ch = *pstrText++)!=_T('\0') ) {
         switch( ch ) {
         case _T('\''):
         case _T('\"'):
            if( State == STATE_SCRIPT ) {
               chString = ch;
               State = STATE_STRING;
               *pT++ = ch;
               *pT++ = _T('\\');
               *pT++ = _T('I');
               *pT++ = _T(' ');
            }
            else if( State == STATE_STRING && ch==chString ) {
               State = STATE_SCRIPT;
               *pT++ = _T('\\');
               *pT++ = _T('I');
               *pT++ = _T('0');
               *pT++ = _T(' ');
               *pT++ = ch;
            }
            else {
               *pT++ = ch;
            }
            break;
         case _T('\\'):
            bWasDelim = CheckDelim(pstrText, pT, ch, State);
            *pT++ = _T('\\');
            *pT++ = _T('\\');
            if( State=STATE_STRING ) {
               // Handle escape character. Copy next character
               // raw.
               // BUG: In the case of special characters (like "{",
               //      this will fail.
               *pT++ = *pstrText++;
            };
            break;
         case _T('{'):
            bWasDelim = CheckDelim(pstrText, pT, ch, State);
            *pT++ = _T('\\');
            *pT++ = _T('{');
            break;
         case _T('}'):
            bWasDelim = CheckDelim(pstrText, pT, ch, State);
            *pT++ = _T('\\');
            *pT++ = _T('}');
            break;
         case _T('/'):
            if( State==STATE_SCRIPT && *pstrText==_T('/') ) {
               State = STATE_COMMENT_SL;
               *pT++ = _T('\\');
               *pT++ = _T('c');
               *pT++ = _T('f');
               *pT++ = _T('4');
               *pT++ = _T(' ');
               *pT++ = ch;
               bWasDelim = true;
               break;
            }
            if( State==STATE_COMMENT_ML && *(pstrText-2)==_T('*') ) {
               State = STATE_SCRIPT;
               *pT++ = ch;
               *pT++ = _T('\\');
               *pT++ = _T('c');
               *pT++ = _T('f');
               *pT++ = _T('0');
               *pT++ = _T(' ');
               bWasDelim = true;
               break;
            }
            if( State==STATE_SCRIPT && *pstrText==_T('*') ) {
               State = STATE_COMMENT_ML;
               *pT++ = ch;
               *pT++ = _T('\\');
               *pT++ = _T('c');
               *pT++ = _T('f');
               *pT++ = _T('4');
               *pT++ = _T(' ');
               bWasDelim = true;
               break;
            }
            bWasDelim = CheckDelim(pstrText, pT, ch, State);
            *pT++ = ch;
            break;
         case _T('\r'):
            break;
         case _T('\n'):
            bWasDelim = CheckDelim(pstrText, pT, ch, State);
            if( State==STATE_COMMENT_SL ) {
               State = STATE_SCRIPT;
               *pT++ = _T('\\');
               *pT++ = _T('c');
               *pT++ = _T('f');
               *pT++ = _T('0');
               // fall through...
            };
            *pT++ = _T('\\');
            *pT++ = _T('p');
            *pT++ = _T('a');
            *pT++ = _T('r');
            *pT++ = _T(' ');
            *pT++ = ch;
            break;
         default:
            // Check for keyword
            if( CheckWord(pstrText, pT, ch, State, bWasDelim)==true ) {
               // Was not a keyword, check for delimiter (and do End-Of-Keyword check)
               bWasDelim = CheckDelim(pstrText, pT, ch, State);
               *pT++ = ch;
            }
         }
         if( pT > pEnd ) break; // TODO: Add intelligent string growth
      };
      *pT = _T('\0');
      s.ReleaseBuffer();
      return s;
   };

private:
   static inline bool IsDelim(TCHAR ch)
   {
      static LPCTSTR pstrDelims = _T(" \r\n\t:;.,'\"\\=<>+-*/!|&?~(){}[]^");
      return _tcschr( pstrDelims, ch )!=NULL;
   }

   static bool CheckDelim(LPCTSTR &pstrText, LPTSTR &pT, TCHAR ch, STATE &State)
   {
      bool bIsDelim = IsDelim(ch);
      if( State==STATE_KEYWORD && bIsDelim ) {
         State = STATE_SCRIPT;
         *pT++ = _T('\\');
         *pT++ = _T('c');
         *pT++ = _T('f');
         *pT++ = _T('0');
         *pT++ = _T(' ');
         return true;
      };
      return bIsDelim;
   }

   static bool CheckWord(LPCTSTR &pstrText, LPTSTR &pT, TCHAR ch, STATE &State, bool bWasDelim)
   {
      // All entries must be in alphabetical order!!!
      static LPCTSTR ppstrKeywords[] = {
         _T("break"),
         _T("case"),
         _T("catch"),
         _T("continue"),
         _T("delete"),
         _T("do"),
         _T("else"),
         _T("function"),
         _T("if"),
         _T("in"),
         _T("new"),
         _T("return"),
         _T("switch"),
         _T("throw"),
         _T("try"),
         _T("typeof"),
         _T("valueof"),
         _T("var"),
         _T("void"),
         _T("while"),
         _T("with"),
         NULL };
      static LPCTSTR ppstrFunctions[] = {
         _T("alert"),
         _T("escape"),
         _T("eval"),
         _T("isNaN"),
         _T("parse"),
         _T("parseFloat"),
         _T("parseInt"),
         _T("unescape"),
         NULL };
      static LPCTSTR ppstrObjects[] = {
         _T("Array"),
         _T("Math"),
         _T("Number"),
         _T("Object"),
         _T("document"),
         _T("event"),
         _T("history"),
         _T("location"),
         _T("navigator"),
         _T("screen"),
         _T("this"),
         _T("window"),
         NULL };

      if( State==STATE_SCRIPT && bWasDelim && !IsDelim(ch) ) {
         // Match any keyword, object or function
         LPCTSTR *p;         
         TCHAR cColor;
         bool bWasKeyword = false;
         if( !bWasKeyword ) {
            p = ppstrKeywords;
            while( *p!=NULL ) {
               int keylen = _tcslen(*p);
               int res = _tcsncmp( pstrText-1, *p, keylen );
               if( (res==0) && IsDelim(*(pstrText-1 + keylen)) ) 
               { 
                  bWasKeyword=true; 
                  cColor = _T('1');
                  break; 
               };
               if( res<0 ) break;
               p++;
            };
         }
         if( !bWasKeyword ) {
            p = ppstrFunctions;
            while( *p!=NULL ) {
               int keylen = _tcslen(*p);
               int res = _tcsncmp( pstrText-1, *p, keylen );
               if( (res==0) && IsDelim(*(pstrText-1 + keylen)) ) 
               { 
                  bWasKeyword=true; 
                  cColor = _T('2');
                  break; 
               };
               if( res<0 ) break;
               p++;
            };
         }
         if( !bWasKeyword ) {
            p = ppstrObjects;
            while( *p!=NULL ) {
               int keylen = _tcslen(*p);
               int res = _tcsncmp( pstrText-1, *p, keylen );
               if( (res==0) && (*(pstrText-1 + keylen)==_T('.')) )  // objects have "." members
               { 
                  bWasKeyword=true; 
                  cColor = _T('3');
                  break; 
               };
               if( res<0 ) break;
               p++;
            };
         }
         if( bWasKeyword ) {
            State = STATE_KEYWORD;
            *pT++ = _T('\\');
            *pT++ = _T('c');
            *pT++ = _T('f');
            *pT++ = cColor;
            *pT++ = _T(' ');
            // Might as well copy the whole keyword
            int cnt = _tcslen(*p)-1;
            *pT++ = ch;
            while( cnt-- > 0 ) *pT++ = *pstrText++;
            return false;
         }
         else {
            // Might as well copy the whole word
            *pT++ = ch;
            while( *pstrText && !IsDelim(*pstrText) ) *pT++ = *pstrText++;
            return false;
         }
      };
      return true;
   };

};


#endif // !defined(AFX_JSCRIPTFORMAT_H__20000617_7F07_0D53_5A0C_0080AD509054__INCLUDED_)

